/* NOTICE
 
    Jim - the Java Image Manager, copyright (c) 2005 - 2007 Grant Gardner 
 
    Jim is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Jim is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  
    In addition, as a special exception, you are permitted to link this code
    with the 3rd party libraries required to compile this file and to
    distribute a linked combination including them. You must obey the
    GNU General Public License in all respects for all of the code except for
    those 3rd party libraries that are licenced under terms incompatible with the
    GNU General Public License.  
 
 
 NOTICE */

/*
 * Metadata.java
 *
 * This class is public domain software - that is, you can do whatever you want
 * with it, and include it software that is licensed under the GNU or the
 * BSD license, or whatever other licence you choose, including proprietary
 * closed source licenses.  Similarly, I release this Java version under the
 * same license, though I do ask that you leave this header in tact.
 *
 * If you make modifications to this code that you think would benefit the
 * wider community, please send me a copy and I'll post it on my site.
 *
 * If you make use of this code, I'd appreciate hearing about it.
 *   drew.noakes@drewnoakes.com
 * Latest version of this software kept at
 *   http://drewnoakes.com/
 *
 */

package au.com.lastweekend.jim.io.metadata;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import au.com.lastweekend.jim.io.exif.MetadataRuntimeException;

/**
 * Result from an exif extraction operation, containing all tags, their values and support for retrieving them.
 * 
 * @author Drew Noakes http://drewnoakes.com
 * 
 * @author ggardner - In order to write meta data it is important that we don't lose ANY. So change the way meta data is used.
 *         Directories are added by the readers.
 * 
 * @version $Id: Metadata.java,v 1.3 2006/02/20 12:21:10 ggardner Exp $
 * @author <a href="mailto:grant@lastweekend.com.au"> <Grant Gardner> </a>
 */
@SuppressWarnings("unchecked")
public final class Metadata implements Serializable {

    /**
     * Comment for <code>serialVersionUID</code>
     */
    private static final long serialVersionUID = 7933958120637298410L;

    /**
     * 
     */
    private final HashMap<Class, Directory> _directoryMap;

    /**
     * List of Directory objects set against this object. Keeping a list handy makes creation of an Iterator and counting tags
     * simple.
     */
    private final ArrayList<Directory> _directoryList;

    /**
     * Creates a new instance of Metadata. Package private.
     */
    public Metadata() {

        _directoryMap = new HashMap<Class, Directory>();
        _directoryList = new ArrayList<Directory>();
    }

    // OTHER METHODS

    /**
     * Creates an Iterator over the tag types set against this image, preserving the order in which they were set. Should the same
     * tag have been set more than once, it's first position is maintained, even though the final value is used.
     * 
     * @return an Iterator of tag types set for this image
     */
    public Iterator<Directory> getDirectoryIterator() {

        return _directoryList.iterator();
    }

    /**
     * Returns a count of unique directories in this metadata collection.
     * 
     * @return the number of unique directory types set for this metadata collection
     */
    public int getDirectoryCount() {

        return _directoryList.size();
    }

    public Directory getDirectory(Class type) {

        if (!Directory.class.isAssignableFrom(type)) {
            throw new MetadataRuntimeException("Class type passed to getDirectory must be an implementation of"
                    + Directory.class.getName());
        }
        // check if we've already issued this type of directory
        if (_directoryMap.containsKey(type)) {
            return _directoryMap.get(type);
        }

        return null;
    }

    public void addDirectory(Directory directory) {

        if (directory != null) {
            Class type = directory.getClass();
            _directoryList.add(directory);
            _directoryMap.put(type, directory);
            addDirectories(directory.getSubDirectories());
            addDirectory(directory.getLinkedDirectory());
        }
    }

    public void insertDirectory(int index, Directory directory) {

        if (directory != null) {
            _directoryList.add(index, directory);
            Class type = directory.getClass();
            _directoryMap.put(type, directory);
            addDirectories(directory.getSubDirectories());
            addDirectory(directory.getLinkedDirectory());
        }
    }

    /**
     * Indicates whether a given directory type has been created in this metadata repository.
     * 
     * @param type
     *            the Directory type
     * @return true if the metadata directory has been created
     */
    public boolean containsDirectory(Class type) {

        return _directoryMap.containsKey(type);
    }

    /**
     * Adds a list of directories to the metadata object.
     * 
     * @param list
     */
    private void addDirectories(List list) {

        for (Iterator iter = list.iterator(); iter.hasNext();) {
            Directory directory = (Directory) iter.next();
            addDirectory(directory);
        }
    }
}